package com.yang.gateway.manager;

import com.alibaba.fastjson.JSON;
import io.micrometer.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import org.yang.common.i18n.constant.I18nMessageConstant;
import org.yang.plugins.resource.exception.ForbiddenException;
import org.yang.plugins.resource.exception.UnauthorizedException;
import reactor.core.publisher.Mono;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
import java.util.stream.Collectors;


/**
 * 经过认证管理器JwtAuthenticationManager认证成功后，就需要对令牌进行鉴权，如果该令牌无访问资源的权限，则不允通过。
 *
 * @author: Islands
 * @create: 2024-06-05 21:25
 */
@Slf4j
@Component
public class JwtAccessManager implements ReactiveAuthorizationManager<AuthorizationContext> {
    private static final String BEARER_PREFIX = "Bearer ";
    private static final String AUTHORITIES="authorities";

    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) {
        String authorization = authorizationContext.getExchange().getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
        if(StringUtils.isBlank(authorization)){
            throw new UnauthorizedException(I18nMessageConstant.UNAUTHORIZED);
        }
        List<String> authorities=new ArrayList<>();
        try {
            String token = authorization.substring(BEARER_PREFIX.length());
            Jwt jwt  = JwtHelper.decodeAndVerify(token, new RsaVerifier(Objects.requireNonNull(getPublicKey())));
            Map map = JSON.parseObject(jwt.getClaims(), Map.class);
            authorities = new ArrayList<>((Collection<? extends String>) map.get(AUTHORITIES));
        }catch (RuntimeException e){
            throw new ForbiddenException(I18nMessageConstant.ACCESS_UNAUTHORIZED);
        }
        //认证通过且角色匹配的用户可访问当前路径
        return mono
                //判断是否认证成功
                .filter(Authentication::isAuthenticated)
                //获取认证后的全部权限
                .flatMapIterable(Authentication::getAuthorities)
                .map(GrantedAuthority::getAuthority)
                //如果权限包含则判断为true
                .any(authorities::contains)
                .map(AuthorizationDecision::new)
                .defaultIfEmpty(new AuthorizationDecision(false));
    }



    public static final String PUBLIC_KEY = "publicKey.key";

    /**
     * 获取公钥
     * @return 返回公钥
     */
    private static String getPublicKey(){
        try {
            Resource resource = new ClassPathResource(PUBLIC_KEY);
            InputStreamReader inputStreamReader = new InputStreamReader(resource.getInputStream());
            BufferedReader br = new BufferedReader(inputStreamReader);
            return br.lines().collect(Collectors.joining("\n"));
        }catch (IOException e){
            return null;
        }
    }
}